package cn.mdmm.open.controller;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;

import cn.mdmm.open.commons.MessageConstant;
import cn.mdmm.open.commons.OpenApiCommonConst;
import cn.mdmm.open.commons.OpenApiCommonUtil;
import cn.mdmm.open.entity.app.AppInfoModel;
import cn.mdmm.open.entity.log.ApiAccessLogModel;
import cn.mdmm.open.entity.method.ApiMethodCfgModel;
import cn.mdmm.open.entity.method.ApiMethodModel;
import cn.mdmm.open.service.app.AppInfoService;
import cn.mdmm.open.service.log.ApiAccessLogService;
import cn.mdmm.open.service.method.ApiMethodCfgService;
import cn.mdmm.open.service.method.ApiMethodService;
import cn.modoumama.common.exception.RequiredException;
import cn.modoumama.common.exception.ServiceException;
import cn.modoumama.common.response.Response;
import cn.modoumama.common.utils.DateUtils.TimeMillisecond;
import cn.modoumama.common.utils.Md5Util;
import cn.modoumama.common.utils.MultipartFileUtil;
import cn.modoumama.common.utils.ObjectUtils;
import cn.modoumama.common.utils.StringUtils;
import cn.modoumama.springboot.dubbo.DubboReference;

/**
 * Spring Boot HelloWorld 案例
 *
 * Created by bysocket on 16/4/26.
 */
@RestController
public class ApiController {
	private final Logger logger = LoggerFactory.getLogger(getClass());

	@Autowired
	private DubboReference dubboReference;

	@Reference(version = "1.0")
	AppInfoService appInfoService;
	@Reference(version = "1.0")
	ApiMethodService apiMethodService;
	@Reference(version = "1.0")
	ApiMethodCfgService apiMethodCfgService;
	@Reference(version = "1.0")
	ApiAccessLogService apiAccessLogService;
	/**
	 * 用户登陆的token,保存用户的信息
	 */
	public static final String token_key = "token_key_";

	@RequestMapping(value = "/api")
	public Response api(HttpServletRequest request) {
		Response response = null;
		Long startDate = System.currentTimeMillis();
		ApiAccessLogModel apiLog = new ApiAccessLogModel();
		Map<String, String> paramMap = new HashMap<String, String>();
		Map<String, String> fileMap = new HashMap<String, String>();
		apiLog.setVisitDate(new Date(startDate));
		// 创建一个通用的多部分解析器.
		CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver(
				request.getSession().getServletContext());
		// 设置编码
		commonsMultipartResolver.setDefaultEncoding("utf-8");
		// 判断是否有文件上传
		if (commonsMultipartResolver.isMultipart(request)) {// 有文件上传
			// 将request变成多部分request
			MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;

			// 获取multiRequest 中所有的文件名
			Iterator<String> iter = multiRequest.getFileNames();

			while (iter.hasNext()) {
				String fileName = iter.next().toString();
				MultipartFile file = multiRequest.getFile(fileName);// (String)
																	// iter.next()
				String fileUrl = MultipartFileUtil.saveFile(file);
				fileMap.put(fileName, fileUrl);
			}

		}
		Map<String, String[]> paramArrayMap = request.getParameterMap();
		for (Entry<String, String[]> entity : paramArrayMap.entrySet()) {
			paramMap.put(entity.getKey(), entity.getValue()[0]);
		}

		// 获取语言环境
		int language = 1;
		try {
			apiLog.setIp(OpenApiCommonUtil.getRemoteIp(request));
			if (paramMap.get("language") != null) {
				String languages = paramMap.get("language");
				if (StringUtils.isNotBlank(languages)) {
					language = Integer.parseInt(languages);
				}
			}
		} catch (Exception e) {
			language = 1;
		}
		try {

			String invokeParam = JSON.toJSONString(paramMap);
			logger.info("---入参：" + invokeParam);
			apiLog.setInvokeMethod(paramMap.get(OpenApiCommonConst.STRING_METHOD));
			apiLog.setMethodVer(paramMap.get(OpenApiCommonConst.STRING_VER));
			apiLog.setInvokeParam(invokeParam);

			// 判断系统级参数
			checkSystemParam(paramMap, request, language);

			paramMap.put(OpenApiCommonConst.STRING_IP, OpenApiCommonUtil.getRemoteIp(request));
			String appId = paramMap.get(OpenApiCommonConst.STRING_APP_ID);
			if (StringUtils.isNotBlank(appId)) {
				apiLog.setAppId(Long.parseLong(appId));
			}
			String userId = paramMap.get(OpenApiCommonConst.STRING_USER_ID);
			if (StringUtils.isNotBlank(userId)) {
				apiLog.setUserId(Long.parseLong(userId));
			}
			if (response == null) {
				// 远程调用业务方法
				paramMap.putAll(fileMap);
				response = invokeService(paramMap, language);
			}

		} catch (RequiredException e) {
			String[] params = { e.getFieldName() };
			response = OpenApiCommonUtil.setResponseObjByError(e.getCode(), params, e.getFieldName(), language);
			apiLog.setResultType(3);
			apiLog.setResultCode(response.getErrInfoList().get(0).getErrorCode());
			apiLog.setResultMsg(response.getErrInfoList().get(0).getErrorDes());
		} catch (ServiceException e) {
			response = OpenApiCommonUtil.setResponseObjByError(e.getCode(), null, null, language);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			response = OpenApiCommonUtil.setResponseObjByError(MessageConstant.SYSTEM_ERROR, null, null, language);
		}

		if (response.getSuccessCount() == 0) {
			apiLog.setResultType(2);
			apiLog.setResultCode(response.getErrInfoList().get(0).getErrorCode());
			apiLog.setResultMsg(response.getErrInfoList().get(0).getErrorDes());
		} else {
			apiLog.setResultType(1);
		}
		Long endDate = System.currentTimeMillis();
		String outParam = JSON.toJSONString(response);
		Long visitTimeCost = endDate - startDate;
		apiLog.setVisitTimeCost(visitTimeCost.intValue());
		apiAccessLogService.insert(apiLog);

		logger.info("---出参：" + outParam);
		return response;
	}

	/**
	 * 判断系统级参数
	 * 
	 * @param paramMap
	 * @param request
	 * @return
	 */
	public void checkSystemParam(Map<String, String> paramMap, HttpServletRequest request, int language) {

		/********************** 参数合法性验证 *****************************/
		// 1.format参数校验，为空则默认为xml
		String format = OpenApiCommonConst.FORMAT_XML;
		if (paramMap.containsKey(OpenApiCommonConst.STRING_FORMAT)) {
			format = paramMap.get(OpenApiCommonConst.STRING_FORMAT);
		}
		if (StringUtils.isBlank(format)) {
			format = OpenApiCommonConst.FORMAT_XML;
		}
		if (!OpenApiCommonConst.FORMAT_JSON.equals(format) && !OpenApiCommonConst.FORMAT_XML.equals(format)) {
			String required = OpenApiCommonConst.STRING_FORMAT + " [" + OpenApiCommonConst.FORMAT_JSON + "/"
					+ OpenApiCommonConst.FORMAT_XML + "]";
			throw new RequiredException(required);
		}

		// 2.系统级参数验证【"timestamp", "method", "ver", "sign"】
		for (int i = 0; i < OpenApiCommonConst.SYSTEM_PARAM_ARRAY.length; i++) {
			if (!paramMap.containsKey(OpenApiCommonConst.SYSTEM_PARAM_ARRAY[i])
					|| StringUtils.isBlank(paramMap.get(OpenApiCommonConst.SYSTEM_PARAM_ARRAY[i]))) {
				throw new RequiredException(OpenApiCommonConst.SYSTEM_PARAM_ARRAY[i]);
			}
		}

		// 3.时间校验
		if (StringUtils.isBlank(paramMap.get(OpenApiCommonConst.STRING_TIMESTAMP))) {
			throw new RequiredException(OpenApiCommonConst.STRING_TIMESTAMP);
		} else {
			try {
				Long time = Long.parseLong(paramMap.get(OpenApiCommonConst.STRING_TIMESTAMP));
				Long curr = System.currentTimeMillis();
				// 最大有效时长10分钟
				if ((curr - time) / TimeMillisecond.MINUTE > 10) {
					throw new ServiceException(MessageConstant.TIMESTAMP_INVALID);
				}
			} catch (NumberFormatException e) {
				throw new RequiredException(OpenApiCommonConst.STRING_TIMESTAMP);
			}
		}
		// 4.验证接口名称以及版本号
		ApiMethodModel apiMethodModel = new ApiMethodModel();
		apiMethodModel.setMethod(paramMap.get(OpenApiCommonConst.STRING_METHOD));
		apiMethodModel = apiMethodService.findObjectByModel(apiMethodModel);

		if (apiMethodModel == null) {
			throw new ServiceException(MessageConstant.METHOD_LEVEL_NOT_FOUND);
		}

		ApiMethodCfgModel apiMethodCfg = new ApiMethodCfgModel();
		apiMethodCfg.setMethodId(apiMethodModel.getId());
		apiMethodCfg.setVer(paramMap.get(OpenApiCommonConst.STRING_VER));
		apiMethodCfg = apiMethodCfgService.findObjectByModel(apiMethodCfg);

		if (apiMethodCfg == null) {
			throw new ServiceException(MessageConstant.VER_INVALID);
		}
		apiMethodCfg.setApiMethodModel(apiMethodModel);

		OpenApiCommonConst.allMethodMap.put(
				paramMap.get(OpenApiCommonConst.STRING_METHOD) + "_" + paramMap.get(OpenApiCommonConst.STRING_VER),
				apiMethodCfg);
		switch (apiMethodModel.getAuthLevel()) {
		case OpenApiCommonConst.L1:
			checkAppKey(paramMap);
			getUserToken(paramMap);
			break;
		case OpenApiCommonConst.L2:
			checkAppKeyAndSessionKey(paramMap);
			break;

		case OpenApiCommonConst.L3:
			checkAppKeyAndSessionKey(paramMap);
			break;
		}
	}

	/**
	 * 校验appKey是否合法
	 * 
	 * @param paramMap
	 * @param request
	 * @param format
	 * @param methodLevel
	 * @return
	 */
	private void checkAppKey(Map<String, String> paramMap) {

		String appKey = paramMap.get(OpenApiCommonConst.STRING_APPKEY);

		// 判断appKey是否为空
		if (StringUtils.isEmpty(appKey)) {
			throw new RequiredException(OpenApiCommonConst.STRING_APPKEY);
		}

		// 校验app是否存在
		AppInfoModel appInfo = new AppInfoModel();
		appInfo.setAppKey(appKey);
		appInfo = appInfoService.findObjectByModel(appInfo);
		if (appInfo == null) {
			throw new ServiceException(MessageConstant.APPKEY_INVALID);
		}

		OpenApiCommonConst.allAppInfoMap.put(appInfo.getAppKey(), appInfo);

		// 根据不同级别获取不同的密钥
		String sceretKey = appInfo.getAppSecret();
		// 验证sign、对参数进行排序
		TreeMap<String, String> paramTreeMap = new TreeMap<String, String>();
		Iterator<Entry<String, String>> entryIterator = paramMap.entrySet().iterator();
		while (entryIterator.hasNext()) {
			Entry<String, String> entry = entryIterator.next();
			String keyStr = entry.getKey();
			String value = entry.getValue();
			// sign参数不参与计算
			if (!OpenApiCommonConst.STRING_SIGN.equals(keyStr)) {
				paramTreeMap.put(keyStr, value);
			}
		}
		// 计算sign的值
		String sign = Md5Util.md5Signature(paramTreeMap, sceretKey);

		logger.info(paramMap.get("method") + "---接口sign：" + sign);

		if (StringUtils.isEmpty(sign) || !sign.equals(paramMap.get(OpenApiCommonConst.STRING_SIGN))) {
			throw new ServiceException(MessageConstant.SIGN_INVALID);
		}

		paramMap.put(OpenApiCommonConst.STRING_APPKEY, appInfo.getAppKey());
		paramMap.put(OpenApiCommonConst.STRING_APP_ID, String.valueOf(appInfo.getId()));
	}

	/**
	 * 校验appKey和sessionKey是否合法
	 * 
	 * @param paramMap
	 * @param request
	 * @param format
	 * @param methodLevel
	 * @return
	 */
	private void checkAppKeyAndSessionKey(Map<String, String> paramMap) {
		checkAppKey(paramMap);

		String sessionKey = paramMap.get(OpenApiCommonConst.STRING_SESSIONKEY);

		// 判断sessionKey是否为空
		if (StringUtils.isEmpty(sessionKey)) {
			throw new RequiredException(OpenApiCommonConst.STRING_SESSIONKEY);
		}

		if (getUserToken(sessionKey) == null) {
			throw new ServiceException(MessageConstant.SESSIONKEY_INVALID);
		}
	}

	private Long getUserToken(Map<String, String> paramMap) {
		String sessionKey = paramMap.get(OpenApiCommonConst.STRING_SESSIONKEY);
		Long userId = null;
		if(StringUtils.isNotBlank(sessionKey)){
			userId = getUserToken(sessionKey);
			if (userId != null) {
				paramMap.put(OpenApiCommonConst.STRING_USER_ID, userId + "");
			}
		}
		return userId;
	}

	private Long getUserToken(String sessionKey) {
		String[] hedwigBeanValues = new String[]{"cn.mdmm.user.service.user.UserService","getUserByToken","java.lang.String"};
		Long userId = null;
		try {
			userId = (Long) invokeService(hedwigBeanValues, sessionKey,"1.0");
		} catch (Throwable e) {
			e.printStackTrace();
		}
		return userId;
	}

	/**
	 * 调用ws服务
	 * 
	 * @param paramMap
	 * @param request
	 * @return
	 */
	private Response invokeService(Map<String, String> paramMap, int language) {

		// 调用接口的版本
		String ver = paramMap.get(OpenApiCommonConst.STRING_VER);
		// 调用接口的方法名
		String merthod = paramMap.get(OpenApiCommonConst.STRING_METHOD);

		// 系统级参数校验
		Response response = null;

		// URL 重定向配置的key
		String key = merthod + "_" + ver;
		ApiMethodCfgModel apiMethodInfo = OpenApiCommonConst.allMethodMap.get(key);

		String redirectValue = OpenApiCommonUtil.stringTrim(apiMethodInfo.getCfgValue());

		if (StringUtils.isEmpty(redirectValue)) {
			throw new ServiceException(MessageConstant.REDIRECT_ERROR);
		}
		String[] hedwigBeanValues = redirectValue.split(";");
		if (hedwigBeanValues.length != 3 || StringUtils.isBlank(hedwigBeanValues[0])
				|| StringUtils.isBlank(hedwigBeanValues[1]) || StringUtils.isBlank(hedwigBeanValues[2])) {
			throw new ServiceException(MessageConstant.REDIRECT_ERROR);
		}

		try {
			// 设置request对象
			Class<?> hedwigRequest = Class.forName(OpenApiCommonUtil.stringTrim(hedwigBeanValues[2]));
			Object objRequest = hedwigRequest.newInstance();
			ObjectUtils.copyProperties(objRequest, paramMap);
			response = (Response) invokeService(hedwigBeanValues, objRequest,apiMethodInfo.getVer());
			if (response == null) {
				logger.error("调用服务返回空！");
				String[] params = new String[] { "返回值为空" };
				return OpenApiCommonUtil.setResponseObjByError(MessageConstant.SYSTEM_SERVICE_ERROR, params, null,language);
			}
			// 解析response对象
			return response;
		} catch (RequiredException e) {
			response = new Response();
			response.addErrInfo(e.getCode(), e.getMessage(), e.getFieldName());
		} catch (ServiceException e) {
			response = new Response();
			response.addErrInfo(e.getCode(), e.getMessage(), null);
		} catch (NoSuchBeanDefinitionException noSuchBeanException) {
			String[] params = new String[] { "客户端配置错误" };
			return OpenApiCommonUtil.setResponseObjByError(MessageConstant.REDIRECT_ERROR, params, null, language);
		} catch (Exception exception) {
			logger.error("接口调用失败：", exception);
			return OpenApiCommonUtil.setResponseObjByError(MessageConstant.SYSTEM_SERVICE_ERROR, null, null, language);
		} catch (Throwable ex) {
			logger.info(ex.getMessage(), ex);
			response = OpenApiCommonUtil.setResponseObjByError(MessageConstant.SYSTEM_ERROR, null, null, language);
		}
		return response;
	}

	public Object invokeService(String[] hedwigBeanValues, Object objRequest,String version) throws Throwable {
		try {
			Class<?> clazz = Class.forName(hedwigBeanValues[0]);
			String[] parameterTypeStrs = hedwigBeanValues[2].split(",");
			Object userService1 = dubboReference.getDubboService(clazz, version);
			Class<?>[] parameterTypes = new Class[parameterTypeStrs.length];
			for (int i = 0; i < parameterTypeStrs.length; i++) {
				parameterTypes[i] = Class.forName(parameterTypeStrs[i]);
			}
			Method method = clazz.getMethod(hedwigBeanValues[1], parameterTypes);
			Object responseDate = method.invoke(userService1, objRequest);
			return responseDate;
		}catch (InvocationTargetException e) {
			Throwable target = e.getTargetException();
			throw target;
		}
	}

}
